/*
* Con.java
* Copyright (C) 2003
* 
* $Id: Console.java,v 1.7 2005/06/30 08:36:22 hzi Exp $
*/
/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
using System;
using Defines = Suake2.UI.Defines;
using Globals = Suake2.UI.Globals;
using Cmd = Suake2.UI.game.Cmd;
using Suake2.UI.qcommon;
using Lib = Suake2.UI.util.Lib;
using Vargs = Suake2.UI.util.Vargs;
namespace Suake2.UI.client
{
	
	/// <summary> Console</summary>
	public sealed class Console:Globals
	{
		public class AnonymousClassxcommand_t:xcommand_t
		{
			public override void  execute()
			{
				SCR.EndLoadingPlaque(); // get rid of loading plaque
				
				if (Globals.cl.attractloop)
				{
					Cbuf.AddText("killserver\n");
					return ;
				}
				
				if (Globals.cls.state == Defines.ca_disconnected)
				{
					// start the demo loop again
					Cbuf.AddText("d1\n");
					return ;
				}
				
				Key.ClearTyping();
				Console.ClearNotify();
				
				if (Globals.cls.key_dest == Defines.key_console)
				{
					Menu.ForceMenuOff();
					Cvar.Set("paused", "0");
				}
				else
				{
					Menu.ForceMenuOff();
					Globals.cls.key_dest = Defines.key_console;
					
					if (Cvar.VariableValue("maxclients") == 1 && Globals.server_state != 0)
						Cvar.Set("paused", "1");
				}
			}
		}
		public class AnonymousClassxcommand_t1:xcommand_t
		{
			public override void  execute()
			{
				SupportClass.ArraySupport.Fill(SupportClass.ToByteArray(Globals.con.text), (byte) ' ');
			}
		}
		public class AnonymousClassxcommand_t2:xcommand_t
		{
			public override void  execute()
			{
				
				int l, x;
				int line;
				//UPGRADE_TODO: Class 'java.io.RandomAccessFile' was converted to 'System.IO.FileStream' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioRandomAccessFile'"
				System.IO.FileStream f;
				sbyte[] buffer = new sbyte[1024];
				System.String name;
				
				if (Cmd.Argc() != 2)
				{
					Com.Printf("usage: condump <filename>\n");
					return ;
				}
				
				//Com_sprintf (name, sizeof(name), "%s/%s.txt", FS_Gamedir(),
				// Cmd_Argv(1));
				name = FS.Gamedir() + "/" + Cmd.Argv(1) + ".txt";
				
				Com.Printf("Dumped console text to " + name + ".\n");
				FS.CreatePath(name);
				f = Lib.fopen(name, "rw");
				if (f == null)
				{
					Com.Printf("ERROR: couldn't open.\n");
					return ;
				}
				
				// skip empty lines
				for (l = Suake2.UI.Globals.con.current - jake2.Globals.con.totallines + 1; l <= jake2.Globals.con.current; l++)
				{
					line = (l % jake2.Globals.con.totallines) * jake2.Globals.con.linewidth;
					for (x = 0; x < jake2.Globals.con.linewidth; x++)
						if (jake2.Globals.con.text[line + x] != ' ')
							break;
					if (x != jake2.Globals.con.linewidth)
						break;
				}
				
				// write the remaining lines
				buffer[jake2.Globals.con.linewidth] = 0;
				for (; l <= jake2.Globals.con.current; l++)
				{
					line = (l % jake2.Globals.con.totallines) * jake2.Globals.con.linewidth;
					//strncpy (buffer, line, con.linewidth);
					Array.Copy(jake2.Globals.con.text, line, buffer, 0, jake2.Globals.con.linewidth);
					for (x = Suake2.UI.Globals.con.linewidth - 1; x >= 0; x--)
					{
						if (buffer[x] == ' ')
							buffer[x] = 0;
						else
							break;
					}
					for (x = 0; buffer[x] != 0; x++)
						buffer[x] &= (sbyte) (0x7f);
					
					buffer[x] = (sbyte) '\n';
					// fprintf (f, "%s\n", buffer);
					try
					{
						f.Write(SupportClass.ToByteArray(buffer), 0, x + 1);
					}
					catch (System.IO.IOException e)
					{
					}
				}
				
				Lib.fclose(f);
			}
		}
		public class AnonymousClassxcommand_t3:xcommand_t
		{
			public override void  execute()
			{
				Key.ClearTyping();
				
				if (jake2.Globals.cls.key_dest == jake2.Defines.key_console)
				{
					if (jake2.Globals.cls.state == jake2.Defines.ca_active)
					{
						Menu.ForceMenuOff();
						jake2.Globals.cls.key_dest = Suake2.UI.Defines.key_game;
					}
				}
				else
					jake2.Globals.cls.key_dest = Suake2.UI.Defines.key_console;
				
				jake2.client.Console.ClearNotify();
			}
		}
		public class AnonymousClassxcommand_t4:xcommand_t
		{
			public override void  execute()
			{
				jake2.Globals.chat_team = false;
				jake2.Globals.cls.key_dest = Suake2.UI.Defines.key_message;
			}
		}
		public class AnonymousClassxcommand_t5:xcommand_t
		{
			public override void  execute()
			{
				jake2.Globals.chat_team = true;
				jake2.Globals.cls.key_dest = Suake2.UI.Defines.key_message;
			}
		}
		
		//UPGRADE_NOTE: The initialization of  'ToggleConsole_f' was moved to static method 'jake2.client.Console'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		public static xcommand_t ToggleConsole_f;
		
		//UPGRADE_NOTE: The initialization of  'Clear_f' was moved to static method 'jake2.client.Console'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		public static xcommand_t Clear_f;
		
		//UPGRADE_NOTE: The initialization of  'Dump_f' was moved to static method 'jake2.client.Console'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		public static xcommand_t Dump_f;
		
		/// <summary>  </summary>
		public static void  Init()
		{
			Globals.con.linewidth = - 1;
			
			CheckResize();
			
			Com.Printf("Console initialized.\n");
			
			//
			// register our commands
			//
			Globals.con_notifytime = Cvar.Get("con_notifytime", "3", 0);
			
			Cmd.AddCommand("toggleconsole", ToggleConsole_f);
			Cmd.AddCommand("togglechat", ToggleChat_f);
			Cmd.AddCommand("messagemode", MessageMode_f);
			Cmd.AddCommand("messagemode2", MessageMode2_f);
			Cmd.AddCommand("clear", Clear_f);
			Cmd.AddCommand("condump", Dump_f);
			Globals.con.initialized = true;
		}
		
		/// <summary> If the line width has changed, reformat the buffer.</summary>
		public static void  CheckResize()
		{
			
			int width = (Globals.viddef.width >> 3) - 2;
			if (width > Defines.MAXCMDLINE)
				width = Defines.MAXCMDLINE;
			
			if (width == Globals.con.linewidth)
				return ;
			
			if (width < 1)
			{
				// video hasn't been initialized yet
				width = 38;
				Globals.con.linewidth = width;
				Globals.con.totallines = Defines.CON_TEXTSIZE / Globals.con.linewidth;
				SupportClass.ArraySupport.Fill(SupportClass.ToByteArray(Globals.con.text), (byte) ' ');
			}
			else
			{
				int oldwidth = Globals.con.linewidth;
				Globals.con.linewidth = width;
				int oldtotallines = Globals.con.totallines;
				Globals.con.totallines = Defines.CON_TEXTSIZE / Globals.con.linewidth;
				int numlines = oldtotallines;
				
				if (Globals.con.totallines < numlines)
					numlines = Globals.con.totallines;
				
				int numchars = oldwidth;
				
				if (Globals.con.linewidth < numchars)
					numchars = Globals.con.linewidth;
				
				sbyte[] tbuf = new sbyte[Defines.CON_TEXTSIZE];
				Array.Copy(Globals.con.text, 0, tbuf, 0, Defines.CON_TEXTSIZE);
				SupportClass.ArraySupport.Fill(SupportClass.ToByteArray(Globals.con.text), (byte) ' ');
				
				for (int i = 0; i < numlines; i++)
				{
					for (int j = 0; j < numchars; j++)
					{
						Globals.con.text[(Globals.con.totallines - 1 - i) * Globals.con.linewidth + j] = tbuf[((Globals.con.current - i + oldtotallines) % oldtotallines) * oldwidth + j];
					}
				}
				
				Console.ClearNotify();
			}
			
			Globals.con.current = Globals.con.totallines - 1;
			Globals.con.display = Globals.con.current;
		}
		
		public static void  ClearNotify()
		{
			int i;
			for (i = 0; i < Defines.NUM_CON_TIMES; i++)
				Globals.con.times[i] = 0;
		}
		
		internal static void  DrawString(int x, int y, System.String s)
		{
			for (int i = 0; i < s.Length; i++)
			{
				Globals.re.DrawChar(x, y, s[i]);
				x += 8;
			}
		}
		
		internal static void  DrawAltString(int x, int y, System.String s)
		{
			for (int i = 0; i < s.Length; i++)
			{
				Globals.re.DrawChar(x, y, s[i] ^ 0x80);
				x += 8;
			}
		}
		
		/*
		* ================ Con_ToggleChat_f ================
		*/
		//UPGRADE_NOTE: The initialization of  'ToggleChat_f' was moved to static method 'jake2.client.Console'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		internal static xcommand_t ToggleChat_f;
		
		/*
		* ================ Con_MessageMode_f ================
		*/
		//UPGRADE_NOTE: The initialization of  'MessageMode_f' was moved to static method 'jake2.client.Console'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		internal static xcommand_t MessageMode_f;
		
		/*
		* ================ Con_MessageMode2_f ================
		*/
		//UPGRADE_NOTE: The initialization of  'MessageMode2_f' was moved to static method 'jake2.client.Console'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		internal static xcommand_t MessageMode2_f;
		
		/*
		* =============== Con_Linefeed ===============
		*/
		internal static void  Linefeed()
		{
			Globals.con.x = 0;
			if (Globals.con.display == Globals.con.current)
				Globals.con.display++;
			Globals.con.current++;
			int i = (Globals.con.current % Globals.con.totallines) * Globals.con.linewidth;
			int e = i + Globals.con.linewidth;
			while (i++ < e)
				Globals.con.text[i] = (sbyte) ' ';
		}
		
		/*
		* ================ Con_Print
		* 
		* Handles cursor positioning, line wrapping, etc All console printing must
		* go through this in order to be logged to disk If no console is visible,
		* the text will appear at the top of the game window ================
		*/
		private static int cr;
		
		public static void  Print(System.String txt)
		{
			int y;
			int c, l;
			int mask;
			int txtpos = 0;
			
			if (!con.initialized)
				return ;
			
			if (txt[0] == 1 || txt[0] == 2)
			{
				mask = 128; // go to colored text
				txtpos++;
			}
			else
				mask = 0;
			
			while (txtpos < txt.Length)
			{
				c = txt[txtpos];
				// count word length
				for (l = 0; l < con.linewidth && l < (txt.Length - txtpos); l++)
					if (txt[l + txtpos] <= ' ')
						break;
				
				// word wrap
				if (l != con.linewidth && (con.x + l > con.linewidth))
					con.x = 0;
				
				txtpos++;
				
				if (cr != 0)
				{
					con.current--;
					cr = 0;
				}
				
				if (con.x == 0)
				{
					Console.Linefeed();
					// mark time for transparent overlay
					if (con.current >= 0)
						con.times[con.current % NUM_CON_TIMES] = cls.realtime;
				}
				
				switch (c)
				{
					
					case '\n': 
						con.x = 0;
						break;
					
					
					case '\r': 
						con.x = 0;
						cr = 1;
						break;
					
					
					default:  // display character and advance
						y = con.current % con.totallines;
						con.text[y * con.linewidth + con.x] = (sbyte) (c | mask | con.ormask);
						con.x++;
						if (con.x >= con.linewidth)
							con.x = 0;
						break;
					
				}
			}
		}
		
		/*
		* ============== Con_CenteredPrint ==============
		*/
		internal static void  CenteredPrint(System.String text)
		{
			int l = text.Length;
			l = (con.linewidth - l) / 2;
			if (l < 0)
				l = 0;
			
			System.Text.StringBuilder sb = new System.Text.StringBuilder(1024);
			for (int i = 0; i < l; i++)
				sb.Append(' ');
			sb.Append(text);
			sb.Append('\n');
			
			sb.Length = 1024;
			
			Console.Print(sb.ToString());
		}
		
		/*
		* ==============================================================================
		* 
		* DRAWING
		* 
		* ==============================================================================
		*/
		
		/*
		* ================ Con_DrawInput
		* 
		* The input line scrolls horizontally if typing goes beyond the right edge
		* ================
		*/
		internal static void  DrawInput()
		{
			int i;
			sbyte[] text;
			int start = 0;
			
			if (cls.key_dest == key_menu)
				return ;
			if (cls.key_dest != key_console && cls.state == ca_active)
				return ; // don't draw anything (always draw if not active)
			
			text = key_lines[edit_line];
			
			// add the cursor frame
			text[key_linepos] = (sbyte) (10 + ((int) (cls.realtime >> 8) & 1));
			
			// fill out remainder with spaces
			for (i = key_linepos + 1; i < con.linewidth; i++)
				text[i] = (sbyte) ' ';
			
			// prestep if horizontally scrolling
			if (key_linepos >= con.linewidth)
				start += 1 + key_linepos - con.linewidth;
			
			// draw it
			//		y = con.vislines-16;
			
			for (i = 0; i < con.linewidth; i++)
				re.DrawChar((i + 1) << 3, con.vislines - 22, text[i]);
			
			// remove cursor
			key_lines[edit_line][key_linepos] = 0;
		}
		
		/*
		* ================ Con_DrawNotify
		* 
		* Draws the last few lines of output transparently over the game top
		* ================
		*/
		internal static void  DrawNotify()
		{
			int x, v;
			int text;
			int i;
			int time;
			System.String s;
			int skip;
			
			v = 0;
			for (i = con.current - NUM_CON_TIMES + 1; i <= con.current; i++)
			{
				if (i < 0)
					continue;
				
				//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
				time = (int) con.times[i % NUM_CON_TIMES];
				if (time == 0)
					continue;
				
				time = (int) (cls.realtime - time);
				if (time > con_notifytime.value_Renamed * 1000)
					continue;
				
				text = (i % con.totallines) * con.linewidth;
				
				for (x = 0; x < con.linewidth; x++)
					re.DrawChar((x + 1) << 3, v, con.text[text + x]);
				
				v += 8;
			}
			
			if (cls.key_dest == key_message)
			{
				if (chat_team)
				{
					DrawString(8, v, "say_team:");
					skip = 11;
				}
				else
				{
					DrawString(8, v, "say:");
					skip = 5;
				}
				
				s = chat_buffer;
				if (chat_bufferlen > (viddef.width >> 3) - (skip + 1))
					s = s.Substring(chat_bufferlen - ((viddef.width >> 3) - (skip + 1)));
				
				for (x = 0; x < s.Length; x++)
				{
					re.DrawChar((x + skip) << 3, v, s[x]);
				}
				re.DrawChar((x + skip) << 3, v, (int) (10 + ((cls.realtime >> 8) & 1)));
				v += 8;
			}
			
			if (v != 0)
			{
				SCR.AddDirtyPoint(0, 0);
				SCR.AddDirtyPoint(viddef.width - 1, v);
			}
		}
		
		/*
		* ================ Con_DrawConsole
		* 
		* Draws the console with the solid background ================
		*/
		internal static void  DrawConsole(float frac)
		{
			int i, j, x, y, n;
			int rows;
			int text;
			int row;
			int lines;
			System.String version;
			
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			lines = (int) (viddef.height * frac);
			if (lines <= 0)
				return ;
			
			if (lines > viddef.height)
				lines = viddef.height;
			
			// draw the background
			re.DrawStretchPic(0, - viddef.height + lines, viddef.width, viddef.height, "conback");
			SCR.AddDirtyPoint(0, 0);
			SCR.AddDirtyPoint(viddef.width - 1, lines - 1);
			
			version = Com.sprintf("v%4.2f", new Vargs(1).add(VERSION));
			for (x = 0; x < 5; x++)
				re.DrawChar(viddef.width - 44 + x * 8, lines - 12, 128 + version[x]);
			
			// draw the text
			con.vislines = lines;
			
			rows = (lines - 22) >> 3; // rows of text to draw
			
			y = lines - 30;
			
			// draw from the bottom up
			if (con.display != con.current)
			{
				// draw arrows to show the buffer is backscrolled
				for (x = 0; x < con.linewidth; x += 4)
					re.DrawChar((x + 1) << 3, y, '^');
				
				y -= 8;
				rows--;
			}
			
			row = con.display;
			for (i = 0; i < rows; i++, y -= 8, row--)
			{
				if (row < 0)
					break;
				if (con.current - row >= con.totallines)
					break; // past scrollback wrap point
				
				int first = (row % con.totallines) * con.linewidth;
				
				for (x = 0; x < con.linewidth; x++)
					re.DrawChar((x + 1) << 3, y, con.text[x + first]);
			}
			
			//ZOID
			// draw the download bar
			// figure out width
			if (cls.download != null)
			{
				if ((text = cls.downloadname.LastIndexOf('/')) != 0)
					text++;
				else
					text = 0;
				
				x = con.linewidth - ((con.linewidth * 7) / 40);
				y = x - (cls.downloadname.Length - text) - 8;
				i = con.linewidth / 3;
				System.Text.StringBuilder dlbar = new System.Text.StringBuilder(512);
				if (cls.downloadname.Length - text > i)
				{
					y = x - i - 11;
					int end = text + i - 1;
					;
					dlbar.Append(cls.downloadname.Substring(text, (end) - (text)));
					dlbar.Append("...");
				}
				else
				{
					dlbar.Append(cls.downloadname.Substring(text));
				}
				dlbar.Append(": ");
				dlbar.Append((char) 0x80);
				
				// where's the dot go?
				if (cls.downloadpercent == 0)
					n = 0;
				else
					n = y * cls.downloadpercent / 100;
				
				for (j = 0; j < y; j++)
				{
					if (j == n)
						dlbar.Append((char) 0x83);
					else
						dlbar.Append((char) 0x81);
				}
				dlbar.Append((char) 0x82);
				dlbar.Append((cls.downloadpercent < 10)?" 0":" ");
				dlbar.Append(cls.downloadpercent).Append('%');
				// draw it
				y = con.vislines - 12;
				for (i = 0; i < dlbar.Length; i++)
					re.DrawChar((i + 1) << 3, y, dlbar[i]);
			}
			//ZOID
			
			// draw the input prompt, user text, and cursor if desired
			DrawInput();
		}
		static Console()
		{
			ToggleConsole_f = new AnonymousClassxcommand_t();
			Clear_f = new AnonymousClassxcommand_t1();
			Dump_f = new AnonymousClassxcommand_t2();
			ToggleChat_f = new AnonymousClassxcommand_t3();
			MessageMode_f = new AnonymousClassxcommand_t4();
			MessageMode2_f = new AnonymousClassxcommand_t5();
		}
	}
}